﻿
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using FacebookXmpp.Stanzas;

namespace FacebookXmpp
{
    public class Dashboard : IDisposable
    {
		public bool IsAuthenticated { get; internal set; }

    	public List<string> Errors = new List<string>();
		public List<Person> Friends = new List<Person>();
        public List<string> Online = new List<string>();
        public List<Response> Responses = new List<Response>();
        public List<string> Groups = new List<string>();

        public Action<string> WorkerState = null;

		public Person Me { get; internal set; }

        public int TimeoutMilliseconds = 5 * 1000;

    	internal string FbAppKey;
		internal string FbAccessToken;

    	private StanzaStream _stanzaStream = null;

    	public Dashboard(string fbAppKey, string fbUserAccessToken)
    	{
    		FbAppKey = fbAppKey;
    		FbAccessToken = fbUserAccessToken;

            if (FbAccessToken.StartsWith("access_token="))
                FbAccessToken = fbUserAccessToken.Substring("access_token=".Length);

            WorkerState = _ => { };

    	}

#if DEBUG
		public StringBuilder Debug { get { return _stanzaStream.Debug; } }
#endif

		/// <summary>
		/// Authenticate with Facebook, populate list of friends (initially all offline)
		/// </summary>
		public void Authenticate()
		{
			if (IsAuthenticated)
				return;

			if (_stanzaStream != null)
				_stanzaStream.Dispose();

			_stanzaStream = new StanzaStream(this);
            if (_stanzaStream.Connected)
            {
                _stanzaStream.ClearHandlers();
                _stanzaStream.AddHandler(new XmppHeader());
                _stanzaStream.AddHandler(new StreamStream());

                _stanzaStream.Process();
            }
		}

		/// <summary>
		/// If not authenticated, try to authenticate.
		/// </summary>
		/// <returns>true if authenticated</returns>
		public bool EnsureAuthenticated()
		{
			if (IsAuthenticated)
				return true;
			
			Authenticate();
			return IsAuthenticated;
		}

		/// <summary>
		/// Update list of friends availablity status
		/// </summary>
		public void UpdateFriends()
		{
			if (!EnsureAuthenticated())
				return;

            _stanzaStream.ClearHandlers();
            _stanzaStream.AddHandler(new Presence("Online"));
			_stanzaStream.Process( );
		}

		public void SendMessage(Person person, string text)
		{
            _stanzaStream.Dashboard.AddSent(person, text);
			if (!EnsureAuthenticated())
				return;

            _stanzaStream.ClearHandlers();
			_stanzaStream.AddHandler(new MessageSend( person, text));
			_stanzaStream.Process(  );
		}

        public void PollResponses()
        {
            if (!EnsureAuthenticated())
                return;
			
            _stanzaStream.ClearHandlers();
            _stanzaStream.AddHandler(new Presence("Online"), new StanzaListenCriteria("presence", string.Empty, true));
            _stanzaStream.AddHandler(new Ignore(), new StanzaListenCriteria("iq", "own-message"));
            _stanzaStream.AddHandler(new MessageRecieve());

            _stanzaStream.Process(  );
        }

        public void SendStatus(Status mine)
        {
            if (!EnsureAuthenticated())
                return;

            string status = (mine == Status.Offline ? "Offline" : "Online");

            _stanzaStream.ClearHandlers();
            _stanzaStream.AddHandler(new Presence(status), new StanzaListenCriteria("presence", string.Empty, true));
            _stanzaStream.AddHandler(new Ignore(), new StanzaListenCriteria("iq", "own-message"));
            _stanzaStream.AddHandler(new MessageRecieve());

            _stanzaStream.Process(  );
        }

        public void AddSent(Person to, string text)
        {   
            Responses.Add(new Response() { From = Me, To = to, Message = text, When = DateTime.Now });
        }

        public void AddResponse(string fromId, string text)
        {
            // should probably cache the last 5 hits in a shorter list
            Person from = Friends.FirstOrDefault(p => p.Id == fromId);

            Responses.Add(new Response() { From = from, To = Me, Message = text, When = DateTime.Now });
        }

    	public void Dispose( )
    	{
			if (_stanzaStream != null)
    			_stanzaStream.Dispose();
    	}

    }

    public class Response
    {
        public Person From;
        public Person To;
        public DateTime When;
    	public string Message;
    }

	public class Person
	{
		public Person( Dashboard dashboard)
		{
			StringBuilder thread = new StringBuilder();
			Guid g = Guid.NewGuid();
			foreach (byte b in g.ToByteArray())
				thread.AppendFormat( "{0:x2}", b );

            _dashboard = dashboard;
			Thread = thread.ToString();
		}

        public void SendMessage(string text)
        {
            _dashboard.SendMessage(this, text);
        }

        public List<Response> MessagesWhere(Func<Response,bool> where)
        {
            _dashboard.PollResponses();

            List<Response> responses = new List<Response>();
            foreach (var response in _dashboard.Responses.Where(where))
                    responses.Add(response);

            lock (_dashboard.Responses)
                foreach (var response in responses)
                    _dashboard.Responses.Remove(response);

            return responses;
        }

        internal Dashboard _dashboard;
		public string Id;
		public string Name;
        public Status State;
		internal string Thread;
        public List<string> Groups = new List<string>();
	}

    public enum Status
    {
        Offline = 0, Available, Away
    }
}